package com.ruoyi.common.utils.file;


import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 文件下载工具类
 * 提供安全、健壮的文件下载功能
 *
 * @author llm
 */
@Slf4j
public class FileDownloadUtils {

    /**
     * 默认连接超时时间（毫秒）
     */
    private static final int DEFAULT_CONNECT_TIMEOUT = 10000;

    /**
     * 默认读取超时时间（毫秒）
     */
    private static final int DEFAULT_READ_TIMEOUT = 30000;

    /**
     * 默认最大文件大小（100MB）
     */
    private static final long DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024;

    /**
     * 默认缓冲区大小
     */
    private static final int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * 允许的协议
     */
    private static final List<String> ALLOWED_PROTOCOLS = Arrays.asList("http", "https");


    /**
     * 获取URL中的查询参数的值
     *
     * @param url       URL
     * @param paramName 查询参数名
     */
    public static String getQueryParamValue(String url, String paramName) {
        try {

            // 解析整个 URL
            URI uri = new URI(url);
            String query = uri.getQuery();

            if (query != null) {
                // 拆分参数
                String[] pairs = query.split("&");
                Map<String, String> params = new HashMap<>();

                for (String pair : pairs) {
                    String[] keyValue = pair.split("=", 2);
                    String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8);
                    String value = keyValue.length > 1 ?
                            URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8) : "";
                    params.put(key, value);
                }

                return params.get(paramName);
            } else {
                return "";
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }


    /**
     * 危险文件扩展名（不允许下载）
     */
    private static final List<String> DANGEROUS_EXTENSIONS = Arrays.asList(
            "exe", "bat", "cmd", "com", "pif", "scr", "vbs", "js", "jar", "sh"
    );

    /**
     * 使用默认配置下载文件并转换为MultipartFile
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadFile(String fileUrl, String fileName) throws IOException {
        return downloadFile(fileUrl, fileName, new DownloadConfig());
    }

    /**
     * 流式下载文件并转换为MultipartFile（适用于大文件）
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadFileStream(String fileUrl, String fileName) throws IOException {
        return downloadFileStream(fileUrl, fileName, new DownloadConfig());
    }

    /**
     * 流式下载文件并转换为MultipartFile（适用于大文件）
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @param config   下载配置
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadFileStream(String fileUrl, String fileName, DownloadConfig config) throws IOException {
        // 1. 参数验证和URL清理
        if (StringUtils.isBlank(fileUrl)) {
            throw new IllegalArgumentException("文件URL不能为空");
        }

        // 清理URL - 移除可能的前缀符号
        String cleanUrl = cleanUrl(fileUrl);

        // 2. URL格式验证和创建
        URL url;
        try {
            url = new URL(cleanUrl);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("URL格式错误: " + cleanUrl, e);
        }

        // 3. 协议验证
        if (!ALLOWED_PROTOCOLS.contains(url.getProtocol().toLowerCase())) {
            throw new IllegalArgumentException("不支持的协议: " + url.getProtocol());
        }

        log.info("开始流式下载文件: {}", cleanUrl);

        // 4. 建立连接并获取基本信息
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            // 设置请求属性 - 增强浏览器模拟
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(config.getConnectTimeout());
            connection.setReadTimeout(config.getReadTimeout());
            setupHttpHeaders(connection, config.getUserAgent());

            // 检查响应码 - 支持重定向和其他成功状态码
            int responseCode = connection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK &&
                    responseCode != HttpURLConnection.HTTP_MOVED_TEMP &&
                    responseCode != HttpURLConnection.HTTP_MOVED_PERM &&
                    responseCode != HttpURLConnection.HTTP_SEE_OTHER) {

                log.error("流式下载失败，HTTP响应码: {}，响应消息: {}", responseCode, connection.getResponseMessage());
                throw new IOException("流式下载失败，HTTP响应码: " + responseCode + "，响应消息: " + connection.getResponseMessage());
            }

            // 获取内容长度
            long contentLength = connection.getContentLengthLong();
            if (contentLength > config.getMaxFileSize()) {
                throw new IOException("文件太大: " + contentLength + " bytes，最大允许: " + config.getMaxFileSize() + " bytes");
            }

            // 获取内容类型
            String contentType = connection.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            // 解析文件名
            String finalFileName = resolveFileName(fileUrl, fileName, contentType);

            // 验证文件扩展名
            if (config.isValidateExtension()) {
                validateFileExtension(finalFileName, config.getAllowedExtensions());
            }

            // 流式下载文件内容
            byte[] fileContent = downloadFileContentStream(connection, config.getMaxFileSize(), config.isShowProgress());

            log.info("流式文件下载完成: {} ({}字节)", finalFileName, fileContent.length);

            // 创建MultipartFile对象
            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } catch (IOException e) {
            log.error("流式下载文件失败: {}", fileUrl, e);
            throw new IOException("流式下载文件失败: " + e.getMessage(), e);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 下载文件并转换为MultipartFile
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @param config   下载配置
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadFile(String fileUrl, String fileName, DownloadConfig config) throws IOException {
        // 根据配置自动选择下载方式
        if (config.isUseStreamDownload()) {
            return downloadFileStream(fileUrl, fileName, config);
        }

        // 1. 参数验证和URL清理
        if (StringUtils.isBlank(fileUrl)) {
            throw new IllegalArgumentException("文件URL不能为空");
        }

        // 清理URL - 移除可能的前缀符号
        String cleanUrl = cleanUrl(fileUrl);

        // 2. URL格式验证和创建
        URL url;
        try {
            url = new URL(cleanUrl);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("URL格式错误: " + cleanUrl, e);
        }

        // 3. 协议验证
        if (!ALLOWED_PROTOCOLS.contains(url.getProtocol().toLowerCase())) {
            throw new IllegalArgumentException("不支持的协议: " + url.getProtocol());
        }

        log.info("开始下载文件: {}", cleanUrl);

        // 4. 建立连接并下载
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            // 设置请求属性 - 增强浏览器模拟
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(config.getConnectTimeout());
            connection.setReadTimeout(config.getReadTimeout());
            setupHttpHeaders(connection, config.getUserAgent());

            // 检查响应码 - 支持重定向和其他成功状态码
            int responseCode = connection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK &&
                    responseCode != HttpURLConnection.HTTP_MOVED_TEMP &&
                    responseCode != HttpURLConnection.HTTP_MOVED_PERM &&
                    responseCode != HttpURLConnection.HTTP_SEE_OTHER) {

                log.error("下载失败，HTTP响应码: {}，响应消息: {}", responseCode, connection.getResponseMessage());
                throw new IOException("下载失败，HTTP响应码: " + responseCode + "，响应消息: " + connection.getResponseMessage());
            }

            // 获取内容长度
            long contentLength = connection.getContentLengthLong();
            if (contentLength > config.getMaxFileSize()) {
                throw new IOException("文件太大: " + contentLength + " bytes，最大允许: " + config.getMaxFileSize() + " bytes");
            }

            // 获取内容类型
            String contentType = connection.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            // 解析文件名
            String finalFileName = resolveFileName(fileUrl, fileName, contentType);

            // 验证文件扩展名
            if (config.isValidateExtension()) {
                validateFileExtension(finalFileName, config.getAllowedExtensions());
            }

            // 下载文件内容
            byte[] fileContent = downloadFileContent(connection, config.getMaxFileSize());

            log.info("文件下载完成: {} ({}字节)", finalFileName, fileContent.length);

            // 创建MultipartFile对象
            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } catch (IOException e) {
            log.error("下载文件失败: {}", fileUrl, e);
            throw new IOException("下载文件失败: " + e.getMessage(), e);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 下载文件内容
     */
    private static byte[] downloadFileContent(HttpURLConnection connection, long maxFileSize) throws IOException {
        try (InputStream inputStream = connection.getInputStream();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int bytesRead;
            long totalBytesRead = 0;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                totalBytesRead += bytesRead;

                // 检查文件大小限制
                if (totalBytesRead > maxFileSize) {
                    throw new IOException("文件太大，超过最大限制: " + maxFileSize + " bytes");
                }

                outputStream.write(buffer, 0, bytesRead);
            }

            return outputStream.toByteArray();
        }
    }

    /**
     * 流式下载文件内容（使用更大的缓冲区，更适合大文件）
     */
    private static byte[] downloadFileContentStream(HttpURLConnection connection, long maxFileSize, boolean showProgress) throws IOException {
        // 使用更大的缓冲区提高流式下载效率
        final int streamBufferSize = 64 * 1024; // 64KB 缓冲区

        try (InputStream inputStream = connection.getInputStream();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[streamBufferSize];
            int bytesRead;
            long totalBytesRead = 0;
            long lastLogTime = System.currentTimeMillis();

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                totalBytesRead += bytesRead;

                // 检查文件大小限制
                if (totalBytesRead > maxFileSize) {
                    throw new IOException("文件太大，超过最大限制: " + maxFileSize + " bytes");
                }

                outputStream.write(buffer, 0, bytesRead);

                // 每隔5秒输出一次下载进度（仅在大文件时且开启进度显示时）
                if (showProgress) {
                    long currentTime = System.currentTimeMillis();
                    if (currentTime - lastLogTime > 5000 && totalBytesRead > 10 * 1024 * 1024) { // 大于10MB时输出进度
                        log.info("下载进度: {} MB", totalBytesRead / (1024 * 1024));
                        lastLogTime = currentTime;
                    }
                }
            }

            return outputStream.toByteArray();
        }
    }

    /**
     * 解析文件名
     */
    private static String resolveFileName(String fileUrl, String specifiedFileName, String contentType) {
        if (StringUtils.isNotBlank(specifiedFileName)) {
            return specifiedFileName;
        }

        // 从URL解析文件名
        String urlFileName = extractFileNameFromUrl(fileUrl);
        if (StringUtils.isNotBlank(urlFileName)) {
            return urlFileName;
        }

        // 根据内容类型生成默认文件名
        String extension = MimeTypeUtils.getExtension(contentType);
        if (StringUtils.isBlank(extension)) {
            extension = "bin";
        }

        return "download_" + System.currentTimeMillis() + "." + extension;
    }

    /**
     * 清理URL - 移除无效前缀和特殊字符
     */
    private static String cleanUrl(String url) {
        if (StringUtils.isBlank(url)) {
            return url;
        }

        String cleanedUrl = url.trim();

        // 移除可能的前缀符号 (@, #, 等)
        if (cleanedUrl.startsWith("@")) {
            cleanedUrl = cleanedUrl.substring(1);
        }
        if (cleanedUrl.startsWith("#")) {
            cleanedUrl = cleanedUrl.substring(1);
        }

        // 确保URL以http://或https://开头
        if (!cleanedUrl.toLowerCase().startsWith("http://") &&
                !cleanedUrl.toLowerCase().startsWith("https://")) {
            // 如果没有协议，默认添加http://
            cleanedUrl = "http://" + cleanedUrl;
        }

        return cleanedUrl;
    }

    /**
     * 设置HTTP请求头 - 增强浏览器模拟，特别针对文件服务接口
     */
    private static void setupHttpHeaders(HttpURLConnection connection, String userAgent) {
        // 基本请求头
        connection.setRequestProperty("User-Agent", userAgent);
        connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
        connection.setRequestProperty("Accept-Encoding", "gzip, deflate");
        connection.setRequestProperty("Connection", "keep-alive");
        connection.setRequestProperty("Upgrade-Insecure-Requests", "1");

        // 针对文件下载接口的特殊请求头
        connection.setRequestProperty("Cache-Control", "no-cache");
        connection.setRequestProperty("Pragma", "no-cache");

        // 模拟浏览器行为
        connection.setRequestProperty("Sec-Fetch-Dest", "document");
        connection.setRequestProperty("Sec-Fetch-Mode", "navigate");
        connection.setRequestProperty("Sec-Fetch-Site", "none");
        connection.setRequestProperty("Sec-Fetch-User", "?1");

        // 支持重定向
        connection.setInstanceFollowRedirects(true);

        // 设置DoOutput为false（GET请求）
        connection.setDoOutput(false);
        connection.setDoInput(true);
    }

    /**
     * 从URL中提取文件名
     */
    private static String extractFileNameFromUrl(String fileUrl) {
        try {
            // 首先尝试从查询参数中获取文件名
            if (fileUrl.contains("?")) {
                String queryString = fileUrl.substring(fileUrl.indexOf("?") + 1);
                String[] params = queryString.split("&");
                for (String param : params) {
                    if (param.toLowerCase().startsWith("name=")) {
                        String fileName = param.substring(5); // 跳过 "name="
                        if (StringUtils.isNotBlank(fileName)) {
                            // URL解码
                            return URLDecoder.decode(fileName, StandardCharsets.UTF_8);
                        }
                    }
                }
            }

            // 如果查询参数中没有name，则从路径中提取
            String pathPart = fileUrl;
            if (fileUrl.contains("?")) {
                pathPart = fileUrl.substring(0, fileUrl.indexOf("?"));
            }

            // 提取文件名
            if (pathPart.contains("/")) {
                String fileName = pathPart.substring(pathPart.lastIndexOf("/") + 1);
                if (StringUtils.isNotBlank(fileName) && !fileName.equals("/")) {
                    // URL解码
                    return URLDecoder.decode(fileName, StandardCharsets.UTF_8);
                }
            }
        } catch (Exception e) {
            log.warn("解析文件名失败: {}", fileUrl, e);
        }
        return null;
    }

    /**
     * 验证文件扩展名
     */
    private static void validateFileExtension(String fileName, String[] allowedExtensions) throws IOException {
        if (StringUtils.isBlank(fileName)) {
            return;
        }

        String extension = getFileExtension(fileName).toLowerCase();

        // 检查危险扩展名
        if (DANGEROUS_EXTENSIONS.contains(extension)) {
            throw new IOException("不允许下载的文件类型: " + extension);
        }

        // 检查允许的扩展名
        if (allowedExtensions != null && allowedExtensions.length > 0) {
            boolean allowed = false;
            for (String allowedExt : allowedExtensions) {
                if (allowedExt.equalsIgnoreCase(extension)) {
                    allowed = true;
                    break;
                }
            }
            if (!allowed) {
                throw new IOException("不支持的文件类型: " + extension);
            }
        }
    }

    /**
     * 获取文件扩展名
     */
    private static String getFileExtension(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }

        int lastDotIndex = fileName.lastIndexOf(".");
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex + 1);
        }

        return "";
    }

    /**
     * 创建默认下载配置
     */
    public static DownloadConfig createDefaultConfig() {
        return new DownloadConfig();
    }

    /**
     * 创建自定义下载配置
     */
    public static DownloadConfig createConfig(int connectTimeout, int readTimeout, long maxFileSize) {
        return new DownloadConfig()
                .setConnectTimeout(connectTimeout)
                .setReadTimeout(readTimeout)
                .setMaxFileSize(maxFileSize);
    }

    /**
     * 创建流式下载配置
     */
    public static DownloadConfig createStreamConfig() {
        return new DownloadConfig()
                .setUseStreamDownload(true)
                .setShowProgress(true)
                .setMaxFileSize(500 * 1024 * 1024); // 500MB 适合大文件下载
    }

    /**
     * 创建流式下载配置
     */
    public static DownloadConfig createStreamConfig(long maxFileSize, boolean showProgress) {
        return new DownloadConfig()
                .setUseStreamDownload(true)
                .setShowProgress(showProgress)
                .setMaxFileSize(maxFileSize);
    }

    /**
     * 基于诊断结果的智能下载方法
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadWithDiagnostics(String fileUrl, String fileName) throws IOException {
        log.info("开始诊断并下载文件: {}", fileUrl);

        // 1. 先进行诊断
        FileDownloadDiagnostics.DiagnosticResult diagnostic = FileDownloadDiagnostics.diagnoseUrl(fileUrl);
        log.info("诊断结果: 成功={}, 推荐User-Agent={}",
                diagnostic.hasSuccessfulAttempt(), diagnostic.getRecommendedUserAgent());

        // 2. 多重下载策略，按优先级尝试
        Exception lastException = null;

        // 策略1: 使用诊断成功的配置
        if (diagnostic.hasSuccessfulAttempt()) {
            FileDownloadDiagnostics.DiagnosticAttempt successfulAttempt = diagnostic.getAttempts().stream()
                    .filter(FileDownloadDiagnostics.DiagnosticAttempt::isSuccess)
                    .findFirst()
                    .orElse(null);

            if (successfulAttempt != null) {
                try {
                    log.info("策略1: 使用诊断成功配置下载");

                    // 添加延迟，避免请求过于频繁
                    Thread.sleep(1000);

                    DownloadConfig config = createDefaultConfig()
                            .setConnectTimeout(20000)  // 增加超时时间
                            .setReadTimeout(60000)
                            .setValidateExtension(false)
                            .setUserAgent(successfulAttempt.getUserAgent());

                    return downloadFileWithSimpleMethod(fileUrl, fileName, config);

                } catch (Exception e) {
                    log.warn("策略1失败: {}", e.getMessage());
                    lastException = e;
                }
            }
        }

        // 策略2: 使用简化的请求头
        try {
            log.info("策略2: 使用简化请求头下载");
            Thread.sleep(1000);

            DownloadConfig simpleConfig = createDefaultConfig()
                    .setConnectTimeout(20000)
                    .setReadTimeout(60000)
                    .setValidateExtension(false)
                    .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");

            return downloadFileWithMinimalHeaders(fileUrl, fileName, simpleConfig);

        } catch (Exception e) {
            log.warn("策略2失败: {}", e.getMessage());
            lastException = e;
        }

        // 策略3: 使用最基本的下载方法
        try {
            log.info("策略3: 使用基本下载方法");
            Thread.sleep(1000);

            return downloadFileBasic(fileUrl, fileName);

        } catch (Exception e) {
            log.warn("策略3失败: {}", e.getMessage());
            lastException = e;
        }

        // 策略4: 最后尝试增强FTP接口方法
        try {
            log.info("策略4: 使用增强FTP接口方法");
            return downloadFromFtpInterface(fileUrl, fileName);

        } catch (Exception e) {
            log.error("所有策略都失败了");
            lastException = e;
        }

        // 如果所有策略都失败，抛出最后一个异常
        if (lastException instanceof IOException) {
            throw (IOException) lastException;
        } else {
            throw new IOException("所有下载策略都失败: " + lastException.getMessage());
        }
    }

    /**
     * 使用简化方法下载文件（最小请求头）
     */
    private static MultipartFile downloadFileWithMinimalHeaders(String fileUrl, String fileName, DownloadConfig config) throws IOException {
        String cleanUrl = cleanUrl(fileUrl);
        URL url = new URL(cleanUrl);

        log.info("使用最小请求头下载: {}", cleanUrl);

        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            // 只设置最基本的请求头
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(config.getConnectTimeout());
            connection.setReadTimeout(config.getReadTimeout());
            connection.setRequestProperty("User-Agent", config.getUserAgent());
            connection.setRequestProperty("Accept", "*/*");
            connection.setInstanceFollowRedirects(true);

            int responseCode = connection.getResponseCode();
            log.info("响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("下载失败，HTTP响应码: " + responseCode);
            }

            String contentType = connection.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            String finalFileName = resolveFileName(fileUrl, fileName, contentType);
            byte[] fileContent = downloadFileContent(connection, config.getMaxFileSize());

            log.info("简化方法下载成功: {} ({}字节)", finalFileName, fileContent.length);
            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 最基本的下载方法
     */
    private static MultipartFile downloadFileBasic(String fileUrl, String fileName) throws IOException {
        String cleanUrl = cleanUrl(fileUrl);
        URL url = new URL(cleanUrl);

        log.info("使用基本方法下载: {}", cleanUrl);

        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            // 最基本的设置
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(30000);
            connection.setReadTimeout(60000);
            connection.setRequestProperty("User-Agent", "Java");
            connection.setInstanceFollowRedirects(true);

            int responseCode = connection.getResponseCode();
            log.info("基本方法响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("基本下载失败，HTTP响应码: " + responseCode);
            }

            String contentType = connection.getContentType();
            String finalFileName = resolveFileName(fileUrl, fileName, contentType != null ? contentType : "application/octet-stream");

            byte[] fileContent = downloadFileContent(connection, DEFAULT_MAX_FILE_SIZE);

            log.info("基本方法下载成功: {} ({}字节)", finalFileName, fileContent.length);
            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 使用简化的方法下载文件
     */
    private static MultipartFile downloadFileWithSimpleMethod(String fileUrl, String fileName, DownloadConfig config) throws IOException {
        String cleanUrl = cleanUrl(fileUrl);
        URL url = new URL(cleanUrl);

        log.info("使用简化方法下载: {}", cleanUrl);

        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            connection.setRequestMethod("GET");
            connection.setConnectTimeout(config.getConnectTimeout());
            connection.setReadTimeout(config.getReadTimeout());

            // 简化的请求头
            connection.setRequestProperty("User-Agent", config.getUserAgent());
            connection.setRequestProperty("Accept", "*/*");
            connection.setRequestProperty("Connection", "close");  // 使用短连接
            connection.setInstanceFollowRedirects(true);

            int responseCode = connection.getResponseCode();
            log.info("简化方法响应码: {}", responseCode);

            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("简化下载失败，HTTP响应码: " + responseCode + "，响应消息: " + connection.getResponseMessage());
            }

            String contentType = connection.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            String finalFileName = resolveFileName(fileUrl, fileName, contentType);
            byte[] fileContent = downloadFileContent(connection, config.getMaxFileSize());

            log.info("简化方法下载成功: {} ({}字节)", finalFileName, fileContent.length);
            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 使用指定HTTP方法下载文件
     */
    private static MultipartFile downloadFileWithMethod(String fileUrl, String fileName, DownloadConfig config, String method) throws IOException {
        String cleanUrl = cleanUrl(fileUrl);
        URL url;
        try {
            url = new URL(cleanUrl);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("URL格式错误: " + cleanUrl, e);
        }

        if (!ALLOWED_PROTOCOLS.contains(url.getProtocol().toLowerCase())) {
            throw new IllegalArgumentException("不支持的协议: " + url.getProtocol());
        }

        log.info("开始下载文件: {} (方法: {})", cleanUrl, method);

        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) url.openConnection();

            connection.setRequestMethod(method);
            connection.setConnectTimeout(config.getConnectTimeout());
            connection.setReadTimeout(config.getReadTimeout());
            setupHttpHeaders(connection, config.getUserAgent());

            int responseCode = connection.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK &&
                    responseCode != HttpURLConnection.HTTP_MOVED_TEMP &&
                    responseCode != HttpURLConnection.HTTP_MOVED_PERM &&
                    responseCode != HttpURLConnection.HTTP_SEE_OTHER) {

                log.error("下载失败，HTTP响应码: {}，响应消息: {}", responseCode, connection.getResponseMessage());
                throw new IOException("下载失败，HTTP响应码: " + responseCode + "，响应消息: " + connection.getResponseMessage());
            }

            String contentType = connection.getContentType();
            if (contentType == null) {
                contentType = "application/octet-stream";
            }

            String finalFileName = resolveFileName(fileUrl, fileName, contentType);

            if (config.isValidateExtension()) {
                validateFileExtension(finalFileName, config.getAllowedExtensions());
            }

            byte[] fileContent = downloadFileContent(connection, config.getMaxFileSize());

            log.info("文件下载完成: {} ({}字节)", finalFileName, fileContent.length);

            return new CustomMultipartFile(fileContent, finalFileName, finalFileName, contentType);

        } catch (IOException e) {
            log.error("下载文件失败: {}", fileUrl, e);
            throw new IOException("下载文件失败: " + e.getMessage(), e);
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    /**
     * 专门用于FTP下载接口的增强下载方法
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile downloadFromFtpInterface(String fileUrl, String fileName) throws IOException {
        // 创建专门针对FTP接口的配置
        DownloadConfig ftpConfig = createDefaultConfig()
                .setConnectTimeout(15000)  // 增加连接超时时间
                .setReadTimeout(60000)     // 增加读取超时时间
                .setValidateExtension(false) // 不验证扩展名，因为可能从参数获取
                .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");

        log.info("使用FTP接口专用方法下载文件: {}", fileUrl);

        try {
            return downloadFile(fileUrl, fileName, ftpConfig);
        } catch (Exception e) {
            // 如果普通方法失败，尝试流式下载
            log.warn("普通下载失败，尝试流式下载: {}", e.getMessage());
            DownloadConfig streamConfig = createStreamConfig()
                    .setConnectTimeout(15000)
                    .setReadTimeout(60000)
                    .setValidateExtension(false)
                    .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0");

            return downloadFileStream(fileUrl, fileName, streamConfig);
        }
    }

    /**
     * 智能下载文件（根据URL自动判断使用最适合的下载方式）
     *
     * @param fileUrl  文件URL
     * @param fileName 指定的文件名（可选，为空时从URL解析）
     * @return MultipartFile对象
     * @throws IOException 下载失败时抛出
     */
    public static MultipartFile smartDownloadFile(String fileUrl, String fileName) throws IOException {
        // 先获取文件头信息来判断文件大小
        DownloadConfig probeConfig = new DownloadConfig().setConnectTimeout(5000).setReadTimeout(10000);

        try {
            String cleanUrl = cleanUrl(fileUrl);
            URL url = new URL(cleanUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD"); // 只获取头信息
            connection.setConnectTimeout(probeConfig.getConnectTimeout());
            connection.setReadTimeout(probeConfig.getReadTimeout());
            setupHttpHeaders(connection, probeConfig.getUserAgent());

            long contentLength = connection.getContentLengthLong();
            connection.disconnect();

            // 根据文件大小智能选择下载方式
            DownloadConfig config;
            if (contentLength > 50 * 1024 * 1024) { // 大于50MB使用流式下载
                log.info("检测到大文件 ({} MB)，使用流式下载", contentLength / (1024 * 1024));
                config = createStreamConfig();
            } else {
                log.info("检测到小文件 ({} KB)，使用普通下载", contentLength / 1024);
                config = createDefaultConfig();
            }

            return downloadFile(fileUrl, fileName, config);

        } catch (Exception e) {
            // 如果无法获取文件信息，默认使用普通下载
            log.warn("无法获取文件大小信息，使用默认下载方式: {}", e.getMessage());
            return downloadFile(fileUrl, fileName);
        }
    }

    /**
     * 下载文件配置类
     */
    public static class DownloadConfig {
        private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
        private int readTimeout = DEFAULT_READ_TIMEOUT;
        private long maxFileSize = DEFAULT_MAX_FILE_SIZE;
        private boolean validateExtension = true;
        private String[] allowedExtensions = MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION;
        private String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
        private boolean useStreamDownload = false; // 是否使用流式下载
        private boolean showProgress = true; // 是否显示下载进度（仅对大文件有效）

        // Getter and Setter methods
        public int getConnectTimeout() {
            return connectTimeout;
        }

        public DownloadConfig setConnectTimeout(int connectTimeout) {
            this.connectTimeout = connectTimeout;
            return this;
        }

        public int getReadTimeout() {
            return readTimeout;
        }

        public DownloadConfig setReadTimeout(int readTimeout) {
            this.readTimeout = readTimeout;
            return this;
        }

        public long getMaxFileSize() {
            return maxFileSize;
        }

        public DownloadConfig setMaxFileSize(long maxFileSize) {
            this.maxFileSize = maxFileSize;
            return this;
        }

        public boolean isValidateExtension() {
            return validateExtension;
        }

        public DownloadConfig setValidateExtension(boolean validateExtension) {
            this.validateExtension = validateExtension;
            return this;
        }

        public String[] getAllowedExtensions() {
            return allowedExtensions;
        }

        public DownloadConfig setAllowedExtensions(String[] allowedExtensions) {
            this.allowedExtensions = allowedExtensions;
            return this;
        }

        public String getUserAgent() {
            return userAgent;
        }

        public DownloadConfig setUserAgent(String userAgent) {
            this.userAgent = userAgent;
            return this;
        }

        public boolean isUseStreamDownload() {
            return useStreamDownload;
        }

        public DownloadConfig setUseStreamDownload(boolean useStreamDownload) {
            this.useStreamDownload = useStreamDownload;
            return this;
        }

        public boolean isShowProgress() {
            return showProgress;
        }

        public DownloadConfig setShowProgress(boolean showProgress) {
            this.showProgress = showProgress;
            return this;
        }
    }
} 